package com.bbs.toutiao.utils;

import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.bbs.toutiao.exception.BaseException;
import com.bbs.toutiao.exception.ExceptionEnum;
import com.bbs.toutiao.mapper.clinet.ClientChatMapper;
import com.bbs.toutiao.vo.client.ChatVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 描述: sse创建以及发送，心跳检测
 * 作者： zhangBin
 * 时间： 2024/4/27 21:00
 * 版本： 1.0
 */

@Slf4j
@Component
public class ChatUtil {

    @Resource
    private ClientChatMapper clientChatMapper;

    // 创建一个LRU缓存，最大1000个条目，过期时间1小时
    private static final Cache<String, String> userUuidCache = CacheUtil.newLRUCache(1000, 60 * 60 * 1000); // 最大1000个条目,过期时间1小时
    // 创建一个ConcurrentHashMap，用于存储sseEmitter
    private static final Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();

    //创建后台管理系统的sse
    private static final Map<String, SseEmitter> adminSseEmitterMap = new ConcurrentHashMap<>();

    //创建SSE连接
    public SseEmitter createSSE(String uid) {
        String uuid = null;
        SseEmitter sseEmitter;
        try {
            //先从缓存中获取
            uuid = userUuidCache.get(uid);
        } catch (NullPointerException e) {
            log.info("空值的" + null);
        } finally {
            if (!StrUtil.isBlank(uuid)) {
                sseEmitter = sseEmitterMap.get(uuid);
            } else {
                sseEmitter = null;
            }
        }
        if (ObjUtil.isNull(sseEmitter)) {
            //默认30秒超时,设置为10分钟超时时间
            sseEmitter = new SseEmitterUTF8(10 * 60 * 1000L);
            //完成后回调
            String uuids = IdUtil.randomUUID();
            try {
                Map<String, Object> map = new HashMap<>();
                map.put("code", "OK_SSE");
                map.put("data", "连接成功！");
                map.put("uuid", uuids);
                sseEmitter.send(SseEmitter.event().data(map));
                sseEmitterMap.put(uuids, sseEmitter);
                userUuidCache.put(uid, uuids);
                log.info("连接用户{}连接成功", uid);
            } catch (Exception e) {
                throw new BaseException(ExceptionEnum.SERVER_BUSY);
            }
            sseEmitter.onCompletion(() -> {
                log.info("断开连接用户{}断开连接{}", uid, uuids);
                sseEmitterMap.remove(uuids);
                userUuidCache.remove(uid);
            });
            //超时回调
            sseEmitter.onTimeout(() -> {
                log.info("超时回调，用户{}", uid);
                sseEmitterMap.remove(uuids);
                userUuidCache.remove(uid);
            });
        }
        return sseEmitter;
    }


    //发送消息
    public Boolean sendClientMSG(Integer uid, String message, String uuid, String imgUrl, Integer redirectId) {
        SseEmitter sseEmitter = null;
        String uuids = null;
        try {
            uuids = userUuidCache.get(String.valueOf(redirectId));
            sseEmitter = sseEmitterMap.get(uuids);
        } catch (Exception e) {
            System.out.println("" + e);
        }
        //获取缓存中的对应用户id的uuid用于查找sse
        Date now = DateUtil.date();
        String format = DateUtil.format(now, "yyyy-MM-dd HH:mm:ss");
        if (clientChatMapper.followByUserIdFollowingId(uid, redirectId) < 0) {
            throw new BaseException(ExceptionEnum.NO_FRIEND);
        }
        clientChatMapper.insertChat(uid, message, redirectId, imgUrl, format);
        if (sseEmitter == null || !Objects.equals(uuid, uuids)) {
            return true;
        }
        try {
            ChatVo chatVo = clientChatMapper.selectChatByUserIdNews(uid, redirectId);
            Map<Object, Object> map = new HashMap<>();
            map.put("code", "SEND_OK_MSG");
            map.put("data", chatVo);
            map.put("uuid", uuid);
            sseEmitter.send(SseEmitter.event()
                    .id(uuid)
                    .reconnectTime(30 * 1000L)
                    .data(map));
        } catch (Exception e) {
            sseEmitterMap.remove(uuid);
            userUuidCache.remove(String.valueOf(uid));
            log.info("用户{},消息id:{},推送异常:{}", uid, uuid, e.getMessage());
            sseEmitter.complete();
            return true;
        }
        return true;
    }


    // 每30秒执行一次心跳检测
    @Scheduled(fixedRate = 15000)
    public void sendHeartbeats() {
        userUuidCache.cacheObjIterator().forEachRemaining((entry) -> {
            String userId = entry.getKey();
            String uuid = entry.getValue();
            SseEmitter sseEmitter = sseEmitterMap.get(uuid);
            try {
                if (sseEmitter != null) {
                    Map<String, Object> heartbeatMap = new HashMap<>();
                    heartbeatMap.put("code", "CHECK_IS_ONLINE");
                    heartbeatMap.put("data", "heartbeat");
                    sseEmitter.send(SseEmitter.event().id(uuid).reconnectTime(5 * 1000).data(heartbeatMap));
                    List<ChatVo> followers = clientChatMapper.followsByUserId(Integer.parseInt(userId));
                    followers.forEach(follower -> {
                        String friendUuid = userUuidCache.get(String.valueOf(follower.getReceiverId()));
                        if (friendUuid != null && sseEmitterMap.containsKey(friendUuid)) {
                            // 向在线好友发送在线通知
                            sendFriendUserAll(userId, friendUuid, uuid, String.valueOf(follower.getReceiverId()));
                        } else {
                            // 给自己发送离线通知
                            sendOfflineNotification(sseEmitter, uuid, String.valueOf(follower.getReceiverId()));
                        }
                    });
                }
            } catch (NullPointerException | IOException e) {
                sseEmitterMap.remove(uuid);
                userUuidCache.remove(userId);
                sseEmitter.complete();
                log.info("用户{}心跳检测失败,断开连接", userId);
            }
        });
    }

    /**
     * 发送好在线通知
     *
     * @param userId     用户id
     * @param friendUuid 用户的uuid
     * @param uuid       朋友的uuid
     * @param receiverId 朋友的id
     */
    private void sendFriendUserAll(String userId, String friendUuid, String uuid, String receiverId) {
        // 构造通知数据
        Map<String, Object> notificationMap = new HashMap<>();
        notificationMap.put("code", "FRIEND_ONLINE");
        notificationMap.put("msg", "用户" + userId + "在线");
        notificationMap.put("uid", userId);
        notificationMap.put("senderUuid", uuid); // 添加发送者的标识
        SseEmitter sseEmitter = sseEmitterMap.get(friendUuid);
        try {
            sseEmitter.send(SseEmitter.event().data(notificationMap).reconnectTime(5 * 1000L));
        } catch (Exception ex) {
            sseEmitterMap.remove(friendUuid);
            userUuidCache.remove(receiverId);
            // 异常处理，记录日志，可能需要进一步处理，比如标记该连接为失效
            log.error("上线通知：用户 {}:通知异常", receiverId);
        }
    }

    /**
     * 离线通知
     *
     * @param uuid       用户id
     * @param receiverId 好友id
     */
    private void sendOfflineNotification(SseEmitter sseEmitter, String uuid, String receiverId) {
        // 构造通知数据
        Map<String, Object> notificationMap = new HashMap<>();
        notificationMap.put("code", "OFF_ONLINE");
        notificationMap.put("msg", "用户" + receiverId + "离线");
        notificationMap.put("uid", receiverId);
        notificationMap.put("nowDate", new Date());
        notificationMap.put("senderUuid", ""); // 添加发送者的标识，如果需要的话
        try {
            // 发送离线通知
            sseEmitter.send(SseEmitter.event().data(notificationMap).reconnectTime(5 * 1000L));
        } catch (Exception ex) {
            sseEmitterMap.remove(uuid);
            userUuidCache.remove(receiverId);
            log.error("离线通知：用户 {}:通知异常", receiverId);
        }
    }


    //    创建后台管理系统的sse
    public SseEmitter createAdminSse(String uid) {
        SseEmitter sseEmitter = new SseEmitterUTF8(30 * 1000L);
        adminSseEmitterMap.put(uid, sseEmitter);
        //获取sseEmitterMap当中的在线人数
        sseEmitter.onCompletion(() -> {
            adminSseEmitterMap.remove(uid);
        });
        //超时回调
        sseEmitter.onTimeout(() -> {
            adminSseEmitterMap.remove(uid);
        });
        pushOnlineCount();
        return sseEmitter;
    }

    //推送在线人数

    @Scheduled(fixedRate = 20000)
    public void pushOnlineCount() {
        adminSseEmitterMap.forEach((uid, sseEmitter) -> {
            int onlineCount = userUuidCache.size() + adminSseEmitterMap.size();
            Map<String, Object> map = new HashMap<>();
            map.put("code", "ONLINE_COUNT");
            map.put("onlineCount", onlineCount);
            try {
                // 发送数据到客户端
                sseEmitter.send(SseEmitter.event().id(uid).data(map).reconnectTime(10 * 1000L));
            } catch (IOException e) {
                log.error("发送在线人数到客户端时发生错误,断开连接:" + uid);
                sseEmitter.complete();
                adminSseEmitterMap.remove(uid);
            }
        });

    }
}
